package com.managertrade.job;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.managertrade.common.ThreadPoolEnum;
import com.managertrade.config.OkxTraderCache;
import com.managertrade.dto.CopyTradingSubPositionsHistoryResponse;
import com.managertrade.dto.LeadTraderCurrentPositionsResponseDTO;
import com.managertrade.dto.vo.OkxLeadTraderResponseVO;
import com.managertrade.dao.mysql.entity.UserFollowTrader;
import com.managertrade.dao.mysql.repository.UserFollowTraderRepository;
import com.managertrade.service.impl.TransactionalService;
import com.managertrade.util.OkxPerpClient;
import com.managertrade.util.OkxProxyHttpClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StopWatch;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import static com.managertrade.job.EmaMacdTrendHuntingStrategy.getPositionDirection;

@Component
@Slf4j
public class OkxTraderCacheScheduler {

    @Autowired
    OkxTraderCache okxTraderCache;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private UserFollowTraderRepository userFollowTraderRepository;
    @Autowired
    private TransactionalService transactionalService;


    /**
     * 每 5 分钟拉取一次 OKX 数据更新缓存
     */
    //@Scheduled(fixedRate = 15 * 60 * 1000)
    public void refreshCache() {
        ThreadPoolEnum.CACHE_SINGLE_THREAD_POOL.getExecutor().submit(()->{
            int page = okxTraderCache.getTraderCurrentPage();
            int limit = 20; // 每次抓 100 条
            Map<String, String> paramMap = new HashMap<>();
            try {

                while (true) {
                    List<OkxLeadTraderResponseVO.Trader> allTraders = new ArrayList<>();
                    String url = "https://www.okx.com/api/v5/copytrading/public-lead-traders";
                    paramMap.put("page",String.valueOf(page));
                    paramMap.put("limit",String.valueOf(limit));
                    paramMap.put("instType","SWAP");
                    String withProxy = OkxProxyHttpClient.get(url, paramMap);
                    OkxLeadTraderResponseVO result = objectMapper.readValue(withProxy, OkxLeadTraderResponseVO.class);
                    if (!"0".equals(result.getCode()) || result.getData() == null || result.getData().isEmpty()) {
                        break;
                    }
                    List<OkxLeadTraderResponseVO.Trader> ranks = result.getData().get(0).getRanks();
                    if (ranks == null || ranks.isEmpty()) break;
                    allTraders.addAll(ranks);
                    int totalPage = Integer.parseInt(result.getData().get(0).getTotalPage());
                    if (page >= totalPage) break;
                    page++;
                    Thread.sleep(2000);
                    // 更新缓存
                    okxTraderCache.updateCache(allTraders);
                    log.info("页面page:{}   OKX 交易员缓存已刷新，总条数:{} " ,page,allTraders.size());
                }


            } catch (Exception e) {
                okxTraderCache.updateTraderCurrentPage(page);
                e.printStackTrace();
                log.error("page {}刷新    OKX 交易员缓存失败: " ,page, e.getMessage());
            }
        });

    }

    /**
     * 获取交易员的最新仓位instid，方向,倍数，盈利
     */
    //@Scheduled(cron = "0 0/4 * * * ?")
    public void updateTraderPositionView(){
        //获取关注的跟单员  随机100个进行更新
        List<UserFollowTrader> all = getRandom100NewTraders();
        //交易员仓位信息
        ConcurrentHashMap<String, LeadTraderCurrentPositionsResponseDTO.Position> dataTotal =  new ConcurrentHashMap<>();
        if(CollectionUtils.isEmpty(all)){
            return;
        }
        for(UserFollowTrader trader: all){
            LeadTraderCurrentPositionsResponseDTO currentLeadPositions = null;
            try {
                currentLeadPositions = OkxPerpClient.getCurrentLeadPositions(trader.getLeadTraderId());
                if(currentLeadPositions.getCode().equals("0")&&currentLeadPositions.getData().size()==0){
                    continue;
                }
                Thread.sleep(500);
            } catch (Exception e) {
                log.info("获取交易员的最新仓位instid，方向,倍数，盈利获取数据异常",e.toString());
                continue;
            }

            List<LeadTraderCurrentPositionsResponseDTO.Position> data = currentLeadPositions.getData();
            Optional<LeadTraderCurrentPositionsResponseDTO.Position> latest = data.stream()
                    .filter(k -> !StringUtils.isEmpty(k.getInstId()))
                    .sorted((k1, k2) -> Long.compare(k2.getOpenTime(), k1.getOpenTime()))
                    .findFirst();
            if(latest.isPresent()){
                dataTotal.put(trader.getLeadTraderId(),latest.get());
            }
        }
        if(dataTotal.size()==0){
            return;
        }

        //提取准确率高的交易员数据
        Map<String, OkxLeadTraderResponseVO.Trader> batchCache = okxTraderCache.getBatchCache(dataTotal.keySet().stream().collect(Collectors.toList()));
        //组装交易员最新动态数据
        batchCache.forEach((k, v) -> {
            LeadTraderCurrentPositionsResponseDTO.Position position = dataTotal.get(k);
            v.setUpdate_opentime(position.getOpenTime());
            v.setUpdate_instId(String.valueOf(position.getInstId()));
            v.setUpdate_lever(String.valueOf(position.getLever()));
            v.setUpdate_upl(String.valueOf(position.getUpl()));
            v.setUpdate_side(String.valueOf(getPositionDirection(position)));
        });
        //配置到缓存中
        okxTraderCache.updatetradeInfo(batchCache.values().stream().collect(Collectors.toList()));
        log.info("获取交易员的最新仓位instid，方向,倍数，盈利 更新数量{}",batchCache.size());

        all.stream().forEach(follow->{
            LeadTraderCurrentPositionsResponseDTO.Position position = dataTotal.get(follow.getLeadTraderId());
            if(position!=null){
                follow.setUpdateOpentime(position.getOpenTime());
                follow.setUpdateInstId(String.valueOf(position.getInstId()));
                follow.setUpdateLever(String.valueOf(position.getLever()));
                follow.setUpdateUpl(new BigDecimal(String.valueOf(position.getUpl())));
                follow.setUpdateSide(String.valueOf(getPositionDirection(position)));
                follow.setUpdateTime(LocalDateTime.now());
                OkxLeadTraderResponseVO.Trader trader = batchCache.get(follow.getLeadTraderId());
                if(trader!=null){
                    follow.setWinRatio(trader.getWinRatio());
                }

            }
        });
        userFollowTraderRepository.saveAll(all);
        log.info("更新最新的最新仓位instid，方向,倍数，盈利 更新数量 到数据库条数{}",all.size());
        List<UserFollowTrader> allDataNow = userFollowTraderRepository.findAll();
        if(allDataNow.size()>500){
            int fetch = allDataNow.size() - 500;
            // 按开仓时间排序（null 的放最前面）
            List<UserFollowTrader> sortedByOpenTime = allDataNow.stream()
                    .sorted(Comparator.comparing(
                            UserFollowTrader::getUpdateOpentime,
                            Comparator.nullsFirst(Long::compareTo) // null 优先
                    ))
                    .collect(Collectors.toList());
            // 取需要删除的数量
            List<UserFollowTrader> toDelete = sortedByOpenTime.stream()
                    .limit(fetch)
                    .collect(Collectors.toList());

            if (!CollectionUtils.isEmpty(toDelete)) {
                userFollowTraderRepository.deleteAll(toDelete);
                log.info("清理最早的交易员记录 {} 条", toDelete.size());
            }
        }
    }
    public List<UserFollowTrader> getRandom100NewTraders() {
        // Step2: 查数据库中已有的交易员
        List<UserFollowTrader> existing = userFollowTraderRepository.findAll();
        // Step4: 打乱顺序
        Collections.shuffle(existing);

        // Step5: 取前100条（如果不够就取全部）
        return existing.stream()
                .limit(50)
                .collect(Collectors.toList());
    }

    /**
     * 获取交易员仓位数据，进行计算，取最新的
     */
    //@Scheduled(cron = "0 0/5 * * * ?")
    public void updateUserFollowTrader(){
        // 1. 拉取缓存交易数据
        ConcurrentHashMap<String, OkxLeadTraderResponseVO.Trader> allTradeInfo = okxTraderCache.getAllTradeInfo();
        if (allTradeInfo == null || allTradeInfo.isEmpty()) {
            log.warn("缓存交易数据为空，无法更新跟单用户");
            return;
        }
        List<OkxLeadTraderResponseVO.Trader> random80NewTraders = getRandom80NewTraders(allTradeInfo);
        //获取交易员历史仓位
        Map<String,List<CopyTradingSubPositionsHistoryResponse.SubPosition>> histoyPosition =  new ConcurrentHashMap<>();
        StopWatch watch  = new StopWatch();
        watch.start();
        random80NewTraders.stream().forEach(unicode->{
            try {
                List<CopyTradingSubPositionsHistoryResponse.SubPosition> traderHistory = OkxPerpClient.getTraderHistory(unicode.getUniqueCode(), "");
                if(!CollectionUtils.isEmpty(traderHistory)){
                    histoyPosition.put(unicode.getUniqueCode(),traderHistory);
                }
                Thread.sleep(200);
            } catch (Exception e) {
                log.info("获取数据出现异常{}",e.toString());
            }

        });
        watch.stop();
        log.info("拉取20条历史交易员仓位数据耗时：{} ms",watch.getTotalTimeMillis());
        //获取最近有交易记录的数据的交易员
        Map<String, Double> qualifiedTraders = getQualifiedTraders(histoyPosition);

        // 排序: 先按胜率，再按跟单人数
        List<OkxLeadTraderResponseVO.Trader> top500 = allTradeInfo.values().stream().filter(k->qualifiedTraders.get(k.getUniqueCode())!=null
        ).collect(Collectors.toList());
        log.info("筛选后交易员数量: {}", top500.size());
        if(!CollectionUtils.isEmpty(top500)){
            List<String> collectQuery = top500.stream().map(k -> k.getUniqueCode()).collect(Collectors.toList());
            List<UserFollowTrader> byLeadTraderIdIn = userFollowTraderRepository.findByLeadTraderIdIn(collectQuery);
            // 3. 已存在的 uniqueCode
            Set<String> existingCodes = byLeadTraderIdIn.stream()
                    .map(UserFollowTrader::getLeadTraderId)
                    .collect(Collectors.toSet());

            // 4. 筛选不存在的交易员
            List<UserFollowTrader> newTraders = top500.stream()
                    .filter(trader -> !existingCodes.contains(trader.getUniqueCode()))
                    .map(trader -> {
                        UserFollowTrader entity = new UserFollowTrader();
                        entity.setLeadTraderId(trader.getUniqueCode());
                        entity.setNickName(trader.getNickName());
                        entity.setIsFollowing(Boolean.TRUE);
                        entity.setUpdateTime(LocalDateTime.now());
                        entity.setUserId(1l);
                        entity.setPnl(!StringUtils.isEmpty(trader.getPnl())?new BigDecimal(trader.getPnl()):new BigDecimal(0));
                        entity.setPnlRatio(!StringUtils.isEmpty(trader.getPnlRatio())?new BigDecimal(trader.getPnlRatio()):new BigDecimal(0));
                        return entity;
                    })
                    .collect(Collectors.toList());

            // 5. 保存新交易员到数据库
            if (!CollectionUtils.isEmpty(newTraders)) {
                transactionalService.saveNewTradersInBatch(newTraders);
                log.info("拉取最新胜率的数据{}条数", newTraders.size());
            } else {
                log.info("没有新的交易员需要保存");
            }



        }

    }
    public List<OkxLeadTraderResponseVO.Trader> getRandom80NewTraders(
            ConcurrentHashMap<String, OkxLeadTraderResponseVO.Trader> allTradeInfo) {

        if (allTradeInfo == null || allTradeInfo.isEmpty()) {
            return Collections.emptyList();
        }

        // Step1: 拿到所有 uniqueCode
        List<String> allUniqueCodes = new ArrayList<>(allTradeInfo.keySet());

        // Step2: 查数据库中已有的交易员
        List<UserFollowTrader> existing = userFollowTraderRepository.findByLeadTraderIdIn(allUniqueCodes);
        Set<String> existingCodes = existing.stream()
                .map(UserFollowTrader::getLeadTraderId)
                .collect(Collectors.toSet());

        // Step3: 过滤掉已有的
        List<OkxLeadTraderResponseVO.Trader> newTraders = allUniqueCodes.stream()
                .filter(code -> !existingCodes.contains(code))
                .map(allTradeInfo::get)
                .collect(Collectors.toList());

        if (newTraders.isEmpty()) {
            return Collections.emptyList();
        }

        // Step4: 打乱顺序
        Collections.shuffle(newTraders);

        // Step5: 取前100条（如果不够就取全部）
        return newTraders.stream()
                .limit(200)
                .collect(Collectors.toList());
    }

    public Map<String, Double> getQualifiedTraders(Map<String, List<CopyTradingSubPositionsHistoryResponse.SubPosition>> historyPosition) {
        Map<String, Double> qualifiedTraders = new HashMap<>();
        long threeDaysAgo = System.currentTimeMillis() - 3L * 24 * 60 * 60 * 1000; // 3天前时间戳

        for (Map.Entry<String, List<CopyTradingSubPositionsHistoryResponse.SubPosition>> entry : historyPosition.entrySet()) {
            String traderCode = entry.getKey();
            List<CopyTradingSubPositionsHistoryResponse.SubPosition> positions = entry.getValue();

            if (positions == null || positions.isEmpty()) {
                continue;
            }

            // ====== 历史胜率 ======
            long totalHistory = positions.size();
            long winsHistory = positions.stream()
                    .filter(p -> {
                        try {
                            BigDecimal pnl = new BigDecimal(p.getPnl());
                            return pnl.compareTo(BigDecimal.ZERO) > 0;
                        } catch (Exception e) {
                            return false;
                        }
                    })
                    .count();

            double winRatioHistory = totalHistory > 0 ? (winsHistory * 1.0 / totalHistory) : 0.0;

            // ====== 最近3天 ======
            List<CopyTradingSubPositionsHistoryResponse.SubPosition> recentTrades = positions.stream()
                    .filter(p -> p.getOpenTime() != null && Long.parseLong(p.getOpenTime()) >= threeDaysAgo)
                    .collect(Collectors.toList());

            if (recentTrades.isEmpty()) {
                continue; // 最近3天没交易
            }

            long totalRecent = recentTrades.size();
            long winsRecent = recentTrades.stream()
                    .filter(p -> {
                        try {
                            BigDecimal pnl = new BigDecimal(p.getPnl());
                            return pnl.compareTo(BigDecimal.ZERO) > 0;
                        } catch (Exception e) {
                            return false;
                        }
                    })
                    .count();

            double winRatioRecent = totalRecent > 0 ? (winsRecent * 1.0 / totalRecent) : 0.0;

            // ====== 筛选条件 ======
            if (winRatioRecent >= 0.85 && winRatioHistory >= 0.8) {
                qualifiedTraders.put(traderCode, winRatioRecent);
                log.info("交易员 {} 符合条件: 最近3天胜率 {}%，历史胜率 {}%，最近交易数 {}",
                        traderCode,
                        String.format("%.2f", winRatioRecent * 100),
                        String.format("%.2f", winRatioHistory * 100),
                        totalRecent);
            } else {
                log.info("交易员 {} 不符合条件: 最近3天胜率 {}%，历史胜率 {}%，最近交易数 {}",
                        traderCode,
                        String.format("%.2f", winRatioRecent * 100),
                        String.format("%.2f", winRatioHistory * 100),
                        totalRecent);
            }
        }

        return qualifiedTraders;
    }

    private double parseDouble(String val) {
        try {
            return Double.parseDouble(val);
        } catch (Exception e) {
            return 0.0;
        }
    }

    private int parseInt(String val) {
        try {
            return Integer.parseInt(val);
        } catch (Exception e) {
            return 0;
        }
    }
}

